using System;
using System.Collections.Generic;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;

namespace UniAspect.Aspect
{
    /// <summary>
    /// Simple implementation of aspect insertion using build plan policy
    /// </summary>
    public class AspectInsertionExtension : UnityContainerExtension, IAspectInsertionExtension
    {
        private readonly IDictionary<object, ProxyFactoryPolicy> _factories =
                new Dictionary<object, ProxyFactoryPolicy>();

        protected override void Initialize()
        {
        }

        public IList<IAspect> Aspects(Type type, string name)
        {
            object key = new NamedTypeBuildKey(type, name);

            ProxyFactoryPolicy proxyFactoryPolicy;
            if(!_factories.ContainsKey(key))
            {
                var keyMappingPolicy = Context.Policies.GetNoDefault<IBuildKeyMappingPolicy>(key, false);
                Type originalType = null;

                if(keyMappingPolicy != null)
                {
                    key = keyMappingPolicy.Map(key);
                    originalType = type;
                }

                var planCreator = Context.Policies.Get<IBuildPlanCreatorPolicy>(key);
                proxyFactoryPolicy = new ProxyFactoryPolicy(originalType, planCreator);

                _factories[key] = proxyFactoryPolicy;
                Context.Policies.Set<IBuildPlanPolicy>(proxyFactoryPolicy, key);
            }
            else
            {
                proxyFactoryPolicy = _factories[key];
            }
            return proxyFactoryPolicy.Interceptors;
        }

        public AspectConfigurer For(Type type, string name)
        {
            return new AspectConfigurer(this, type, name);
        }

        public AspectConfigurer For(Type type)
        {
            return For(type, null);
        }

        public AspectConfigurer For<T>(string name)
        {
            return For(typeof(T), name);
        }

        public AspectConfigurer For<T>()
        {
            return For(typeof(T), null);
        }
    }
}
